#include <Windows.h>
#include <tchar.h>
#include "capture.h"
#include "jpeglib.h"

#ifdef _DEBUG
	#pragma comment(lib, "jpegd.lib")
#else
	#pragma comment(lib, "jpeg.lib")
#endif

LONG gScreenWidth, gScreenHeight;
LONG gRemoteWidth, gRemoteHeight;
LONG gRemoteSize, gRemoteLineByte;
LONG gRemoteResetWidth, gRemoteResetHeight;

LONG gPosX, gPosY;
LONG gRatio;

HBITMAP ghBitmap, ghOldBitmap;
HDC	ghDC, ghCDC;
BITMAPINFO gBitmapInfo;

HANDLE gMutexForRemote;
BOOL gRemoteReset;

#define XOR(a, b) (((a)&(~(b)))|((~(a))&(b)))

void init_mutex()
{
	gMutexForRemote = CreateMutex(NULL, FALSE, _T("remote"));
	gRemoteReset = FALSE;
}

void init_capture()
{
	gScreenWidth = GetSystemMetrics(SM_CXSCREEN);
	gScreenHeight = GetSystemMetrics(SM_CYSCREEN);

	gRatio = 1;

	gBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	gBitmapInfo.bmiHeader.biPlanes = 1;
	gBitmapInfo.bmiHeader.biBitCount = 24;
	gBitmapInfo.bmiHeader.biCompression = BI_RGB;
	gBitmapInfo.bmiHeader.biSizeImage = 0;
	gBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
	gBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
	gBitmapInfo.bmiHeader.biClrUsed = 0;
	gBitmapInfo.bmiHeader.biClrImportant = 0;
	gBitmapInfo.bmiColors[0].rgbBlue = 8;
	gBitmapInfo.bmiColors[0].rgbGreen = 8;
	gBitmapInfo.bmiColors[0].rgbRed = 8;
	gBitmapInfo.bmiColors[0].rgbReserved = 0;
}

void capture_screen(BYTE *pImage)
{
	gBitmapInfo.bmiHeader.biWidth = gRemoteWidth;
	gBitmapInfo.bmiHeader.biHeight = -gRemoteHeight;
	BitBlt(ghCDC, 0, 0, gRemoteWidth, gRemoteHeight, NULL, 0, 0, BLACKNESS);
	StretchBlt(ghCDC, 0, 0, gRemoteWidth, gRemoteHeight, ghDC, 
		gPosX, gPosY, gRemoteWidth * gRatio, gRemoteHeight * gRatio, SRCCOPY);
	GetDIBits(ghCDC, ghBitmap, 0, gRemoteHeight, pImage, &gBitmapInfo, DIB_RGB_COLORS);
}

void bgr2rgb(byte *image, int len)
{
	for (int i = 0; i < len; i+=3) {
		image[i] = XOR(image[i], image[i+2]);
		image[i+2] = XOR(image[i], image[i+2]);
		image[i] = XOR(image[i], image[i+2]);
	}
}

int compress_buffer(BYTE *pImage, BYTE *pCompressedImage)
{
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;

	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);

	// set destination
	unsigned char *jpeg_buffer = 0;
	unsigned long out_size = 0;
	jpeg_mem_dest(&cinfo, &jpeg_buffer, &out_size);

	// Set parameters for compression
	cinfo.image_width = gRemoteWidth;
	cinfo.image_height = gRemoteHeight;
	cinfo.input_components = 3;
	cinfo.in_color_space = JCS_RGB;

	jpeg_set_defaults(&cinfo);
	jpeg_start_compress(&cinfo, TRUE);

	JSAMPROW row_pointer[1];	/* pointer to a single row */
	int row_stride;			/* physical row width in buffer */

	row_stride = cinfo.image_width * cinfo.input_components;	/* JSAMPLEs per row in image_buffer */

	while (cinfo.next_scanline < cinfo.image_height) {
		byte *row_buffer = &((unsigned char *)pImage)[cinfo.next_scanline * gRemoteLineByte];
		bgr2rgb(row_buffer, gRemoteLineByte);
		row_pointer[0] = row_buffer;
		jpeg_write_scanlines(&cinfo, row_pointer, 1);
	}

	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	// clear memory
	memcpy(pCompressedImage, jpeg_buffer, out_size);
	free(jpeg_buffer);

	return out_size;
}

void set_remote_pos(LONG x, LONG y)
{
	gPosX = x;
	gPosY = y;
}

void set_remote_size(LONG width, LONG height)
{
	WaitForSingleObject(gMutexForRemote, INFINITE);
		gRemoteResetWidth = width;
		gRemoteResetHeight = height;
		gRemoteReset = TRUE;
	ReleaseMutex(gMutexForRemote);
}

#include <stdio.h>

void clean_remote_data(BYTE *pImage, BYTE *pCompressedImage)
{
	printf("clean\n");
	SelectObject(ghCDC, ghOldBitmap);
	DeleteObject(ghBitmap);
	if (pImage != NULL) {
		delete[] pImage;
		pImage = NULL;
	}
	if (pCompressedImage != NULL) {
		delete[] pCompressedImage;
		pCompressedImage = NULL;
	}
}

void reset_remote_size(BYTE **ppImage, BYTE **ppCompressedImage)
{
	// for capture & compress
	WaitForSingleObject(gMutexForRemote, INFINITE);
		gRemoteWidth = gRemoteResetWidth;
		gRemoteHeight = gRemoteResetHeight;
		gRemoteReset = FALSE;
	ReleaseMutex(gMutexForRemote);

	gRemoteLineByte = 4 - gRemoteWidth * 3 % 4;
	gRemoteLineByte = gRemoteLineByte == 4 ? gRemoteWidth * 3 : gRemoteWidth * 3 + gRemoteLineByte;

	gRemoteSize = gRemoteLineByte * gRemoteHeight;

	*ppImage = new BYTE[gRemoteSize];
	*ppCompressedImage = new BYTE[gRemoteSize];

	ghDC = GetDC(NULL);
	ghCDC = CreateCompatibleDC(ghDC);
	ghBitmap = CreateCompatibleBitmap(ghDC, gRemoteWidth, gRemoteHeight);
	ghOldBitmap = (HBITMAP)SelectObject(ghCDC, ghBitmap);
}

BOOL has_remote_reset()
{
	BOOL ret;
	WaitForSingleObject(gMutexForRemote, INFINITE);
		ret = gRemoteReset;
	ReleaseMutex(gMutexForRemote);

	return ret;
}
